//$Id$

package builders;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

/**
 * This is the ATD for the following classes:
 * BuildingManager
 * Building
 * BuildingRequirement
 * 
 * @author Vincent
 */
public class BuildingATD {

	BuildingManager buildingManager;
	File inputFile;
	File expectedOutputFile;
	Building[] manager;
	int testCount;
	int errorCount;

	/**
	 * This is the constructor of the BuildingATD class.
	 * 
	 * @param inputFile				specify the file name of the file from which inputs are read
	 * @param expectedOutputFile	specify the file name of the file from which expected outputs are read
	 */
	BuildingATD(String inputFile, String expectedOutputFile) {
		this.buildingManager = new BuildingManager("testDataForBuilding.txt");
		this.inputFile = new File(inputFile);
		this.expectedOutputFile = new File(expectedOutputFile);
		this.manager = buildingManager.getManager();
	}

	/*
	 * this method will read each test cases from the file specified by inputFile and compare the results from the test cases against the expected outputs.
	 * the expected outputs are read from the file specified by expectedOutputFile.
	 * 
	 * the test will end when the line read from the input file is "end of test" .
	 * 
	 * this method will read the test line provided in the input file and match it to a method call that is from one of the three classes being tested.
	 */
	public void executeTestCases(){

		try {
			FileReader inputFileReader = new FileReader(inputFile);
			BufferedReader inputReader = new BufferedReader (inputFileReader);

			FileReader expectedOutputFileReader = new FileReader(expectedOutputFile);
			BufferedReader expectedOutputReader = new BufferedReader (expectedOutputFileReader);

			String line = inputReader.readLine();
			System.out.println(line);

			while (!(line = inputReader.readLine()).equals("end of test")){
				String testDescription = line;
				System.out.println(testDescription);

				String testLine = inputReader.readLine();

				int methodNumber = matchTestLineToMethodCall(testLine);

				switch (methodNumber) {
				case 1: 
					checkBuildingGetterMethods(inputReader, expectedOutputReader);
					break;
				case 2:
					checkBuildingManagerGetTotalNumberOfBuildingsAvailable(inputReader, expectedOutputReader);
					break;
				case 3:
					checkBuildingRequirementGetterMethods(inputReader, expectedOutputReader);
					break;
				case 4:
					checkBuildings(inputReader, expectedOutputReader);
					break;
				case 5:
					checkBuildingManagerStringToBuilding(inputReader, expectedOutputReader);
					break;
				case 6:
					checkBuildingDecreaseNumberOfBuildingsAvailable(inputReader, expectedOutputReader);
					break;
				}

			}

			System.out.println(line);
			System.out.println("Total number of test cases: " + testCount);
			System.out.println("Total number of errors: " + errorCount);

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * @param testLine	the test line that is read from the input file
	 * @return			an integer which will correspond to a method from one of the three classes being tested
	 */
	private int matchTestLineToMethodCall(String testLine) {
		int methodNumber = 0;

		if (testLine.equals("check getter methods in Building")){
			methodNumber = 1;
		} else if (testLine.equals("BuildingManager getTotalNumberOfBuildingsAvailable()")){
			methodNumber = 2;
		} else if (testLine.equals("check the getter methods in BuildingRequirement")){
			methodNumber = 3;
		} else if (testLine.equals("print all buildings")){
			methodNumber = 4;
		} else if (testLine.equals("BuildingManager stringToBuilding(String)")){
			methodNumber = 5;
		} else if (testLine.equals("Building decreaseNumberOfBuildingsAvailable()")){
			methodNumber = 6;
		}
		return methodNumber;
	}

	/**
	 * @param errorMessage	the error message that is read from the input file
	 * @param expected		the expected result that is read from the expected output file
	 * @param result		the result from the testing of the method
	 */
	private void printErrorMessage(String errorMessage,
			String expected, String result) {
		System.out.println(errorMessage);
		System.out.println("expected: " + expected);
		System.out.println("result: " + result);
		errorCount++;
	}

	/** 
	 * @param errorMessage		the error message this is to be printed if the test case failed
	 * @param expectedResult	the expected result that is supposed to be produce in the test case
	 * @param result			the result from doing the test case
	 */
	private void doTestCase(String errorMessage, String expectedResult, String result) {
		if (!expectedResult.equals(result)){
			printErrorMessage(errorMessage, expectedResult, result);
		}
		testCount++;
	}
	
	/*
	 * the format used to create the following methods which are used for testing:
	 * 1) the error messages from the input file are read
	 * 2) any input data needed for testing are read from the input file
	 * 3) if necessary, those input data that need to be convert to other types are done
	 * 3.1) if necessary, creation of new objects are done
	 * 4) the expected result are read from the expected output file
	 * 4.1) for each expected result, it is compared to the result from calling the method that is being tested
	 * 5) the 'end of test' message is read from expected output file to indicate that the test case is done 
	 */

	/*
	 * this test case will check all the getter methods in Building.
	 * 
	 * The tested getter methods:
	 * -getName
	 * -getImageFile
	 * -getPoints
	 * -getNumberOfBuildingsAvailable
	 */
	private void checkBuildingGetterMethods(BufferedReader inputReader,
			BufferedReader expectedOutputReader) {

		try {
			String errorBuildingName = inputReader.readLine();
			String errorBuildingImageFile = inputReader.readLine();
			String errorBuildingPoints = inputReader.readLine();
			String errorNumberOfBuildingAvailable = inputReader.readLine();

			String buildingName = inputReader.readLine(); 
			String buildingImageFile = inputReader.readLine();
			String buildingPoints = inputReader.readLine();
			String numberOfBuildingAvailable = inputReader.readLine();			
			String stringNumberOfBuildingRequirement = inputReader.readLine();
			
			int numberOfBuildingRequirement = Integer.valueOf(stringNumberOfBuildingRequirement);
			BuildingRequirement[] buildingRequirement = buildingManager.createBuildingRequirementArray(numberOfBuildingRequirement);

			int resourcesAdded = 0; 
			
			while (resourcesAdded <  numberOfBuildingRequirement){

				String resource = inputReader.readLine(); 
				String resourceAmount = inputReader.readLine(); 

				buildingManager.setResource(buildingRequirement, resourcesAdded, resource, resourceAmount);
				resourcesAdded++;
			}

			Building newBuilding = new Building(buildingName, buildingImageFile, buildingPoints, numberOfBuildingAvailable, buildingRequirement);

			doTestCase(errorBuildingName, expectedOutputReader.readLine(), newBuilding.getName());
			doTestCase(errorBuildingImageFile, expectedOutputReader.readLine(), newBuilding.getImageFile());
			doTestCase(errorBuildingPoints, expectedOutputReader.readLine(), Integer.toString(newBuilding.getPoints()));
			doTestCase(errorNumberOfBuildingAvailable, expectedOutputReader.readLine(), Integer.toString(newBuilding.getNumberOfBuildingsAvailable()));

			// end of test
			expectedOutputReader.readLine();

		} catch (IOException e) {
			e.printStackTrace();
		} catch (NumberFormatException e) {
			e.printStackTrace();
		} 
	}

	/*
	 * This test case will check the method getTotalNumberOfBuildingsAvailable from BuildingManager.
	 *
	 * the method getTotalNumberOfBuildingsAvailable is used to verify that the method getTotalNumberOfBuildingsAvailable is working correctly.
	 */
	private void checkBuildingManagerGetTotalNumberOfBuildingsAvailable(BufferedReader inputReader,
			BufferedReader expectedOutputReader) {

		try {
			String errorMessageTestData = inputReader.readLine();
			String errorMessageDefaultData = inputReader.readLine();

			String stringTestFileName = inputReader.readLine();

			BuildingManager newBuildingManager = new BuildingManager(stringTestFileName);

			doTestCase(errorMessageTestData, expectedOutputReader.readLine(), Integer.toString(newBuildingManager.getTotalNumberOfBuildingsAvailable()));
			doTestCase(errorMessageDefaultData, expectedOutputReader.readLine(), Integer.toString(buildingManager.getTotalNumberOfBuildingsAvailable()));

			// end of test
			expectedOutputReader.readLine();

		}catch (IOException e) {
			e.printStackTrace();
		} catch (NumberFormatException e) {
			e.printStackTrace();
		} 
	}

	/*
	 * this test case will check all the getter methods in BuildingRequirement.
	 * 
	 * The tested getter methods:
	 * -getResources
	 * -getAmount
	 */
	private void checkBuildingRequirementGetterMethods(BufferedReader inputReader, BufferedReader expectedOutputReader) {

		try {
			String errorResource = inputReader.readLine();
			String errorAmount = inputReader.readLine();

			String stringResource = inputReader.readLine();
			String stringAmount = inputReader.readLine();

			int amount = Integer.valueOf(stringAmount);
			Player.Resource resource = buildingManager.searchResource(stringResource);

			BuildingRequirement newBuildingRequirement = new BuildingRequirement(resource, amount); 

			doTestCase(errorResource, expectedOutputReader.readLine(), newBuildingRequirement.getResources().toString());
			doTestCase(errorAmount, expectedOutputReader.readLine(), Integer.toString(newBuildingRequirement.getAmount()));

			// end of test
			expectedOutputReader.readLine();

		}catch (IOException e) {
			e.printStackTrace();
		} catch (NumberFormatException e) {
			e.printStackTrace();
		} 
	}

	/*
	 * this test case will check that all the data from the input file are loaded correctly by the BuildingManager.
	 */
	private void checkBuildings(BufferedReader inputReader,
			BufferedReader expectedOutputReader) {

		try {
			String errorBuildingName = inputReader.readLine();
			String errorBuildingImageFile = inputReader.readLine();
			String errorBuildingPoints = inputReader.readLine();
			String errorNumberOfBuildingAvailable = inputReader.readLine();
			String errorBuildingRequirementlist = inputReader.readLine();
			String errorBuildingRequirement = inputReader.readLine();

			for (int i = 0; i < BuildingManager.TOTAL_NUMBER_OF_BUILDING_IN_GAME; i++){

				doTestCase(errorBuildingName + i, expectedOutputReader.readLine(), manager[i].getName());
				doTestCase(errorBuildingImageFile + i, expectedOutputReader.readLine(), manager[i].getImageFile());
				doTestCase(errorBuildingPoints + i, expectedOutputReader.readLine(), Integer.toString(manager[i].getPoints()));
				doTestCase(errorNumberOfBuildingAvailable + i, expectedOutputReader.readLine(), Integer.toString(manager[i].getNumberOfBuildingsAvailable()));

				BuildingRequirement[] buildingRequirement = manager[i].getBuildingRequirementList();
				int numberOfBuildingRequirement = buildingRequirement.length;

				doTestCase(errorBuildingRequirementlist + i, expectedOutputReader.readLine(), Integer.toString(numberOfBuildingRequirement));

				int pointer = 0;
				while (pointer < numberOfBuildingRequirement){

					doTestCase(errorBuildingRequirement + i + " at position " + pointer, expectedOutputReader.readLine(), buildingRequirement[pointer].getResources().toString());
					doTestCase(errorBuildingRequirement + i + " at position " + pointer, expectedOutputReader.readLine(), Integer.toString(buildingRequirement[pointer].getAmount()));

					pointer++;
				}
			}

			// end of test
			expectedOutputReader.readLine();

		} catch (IOException e) {
			e.printStackTrace();
		} catch (NumberFormatException e) {
			e.printStackTrace();
		} 
	}

	/*
	 * this test case will check the method stringToBuilding from BuildingManager.
	 * 
	 * the method getNumberOfBuildingsAvailable is used to verify that the method stringToBuilding is working correctly.
	 */
	private void checkBuildingManagerStringToBuilding(BufferedReader inputReader,
			BufferedReader expectedOutputReader) {

		try {
			String errorMessage = inputReader.readLine();

			String stringName1 = inputReader.readLine();
			String stringName2 = inputReader.readLine();

			doTestCase(errorMessage + stringName1, expectedOutputReader.readLine(), Integer.toString(buildingManager.stringToBuilding(stringName1).getNumberOfBuildingsAvailable()));
			doTestCase(errorMessage + stringName2, expectedOutputReader.readLine(), Integer.toString(buildingManager.stringToBuilding(stringName2).getNumberOfBuildingsAvailable()));

			// end of test
			expectedOutputReader.readLine();

		} catch (IOException e) {
			e.printStackTrace();
		} catch (NumberFormatException e) {
			e.printStackTrace();
		} 
	}

	/*
	 * this test case will check the method decreaseNumberOfBuildingsAvailable from Building.
	 * 
	 * the method getNumberOfBuildingsAvailable is used to verify that the method decreaseNumberOfBuildingsAvailable is working correctly.
	 */
	private void checkBuildingDecreaseNumberOfBuildingsAvailable(BufferedReader inputReader,
			BufferedReader expectedOutputReader) {

		try {
			String errorMessage = inputReader.readLine();

			String buildingName = inputReader.readLine(); 
			String buildingImageFile = inputReader.readLine();
			String buildingPoints = inputReader.readLine();
			String numberOfBuildingAvailable = inputReader.readLine();

			String stringNumberOfBuildingRequirement = inputReader.readLine();
			
			int numberOfBuildingRequirement = Integer.valueOf(stringNumberOfBuildingRequirement);
			BuildingRequirement[] buildingRequirement = buildingManager.createBuildingRequirementArray(numberOfBuildingRequirement);

			int resourcesAdded = 0; 
			
			while (resourcesAdded <  numberOfBuildingRequirement){

				String resource = inputReader.readLine(); 
				String resourceAmount = inputReader.readLine(); 

				buildingManager.setResource(buildingRequirement, resourcesAdded, resource, resourceAmount);
				resourcesAdded++;
			}
			
			Building newBuilding = new Building(buildingName, buildingImageFile, buildingPoints, numberOfBuildingAvailable, buildingRequirement);

			doTestCase(errorMessage, expectedOutputReader.readLine(), new Boolean(newBuilding.decreaseNumberOfBuildingsAvailable()).toString());
			doTestCase(errorMessage, expectedOutputReader.readLine(), Integer.toString(newBuilding.getNumberOfBuildingsAvailable()));
			doTestCase(errorMessage, expectedOutputReader.readLine(), new Boolean(newBuilding.decreaseNumberOfBuildingsAvailable()).toString());
			doTestCase(errorMessage, expectedOutputReader.readLine(), Integer.toString(newBuilding.getNumberOfBuildingsAvailable()));

			// end of test
			expectedOutputReader.readLine();

		} catch (IOException e) {
			e.printStackTrace();
		} catch (NumberFormatException e) {
			e.printStackTrace();
		} 
	}

	/*
	 * the input file name: testcasesForBuilding.txt
	 * the expected output file name: expectedResultsForBuilding.txt
	 * the data used for Building Manager: testDataForBuilding.txt
	 */
	public static void main(String[] args) throws Exception {
		String workingDir = System.getProperty("user.dir");
		String inputFile = workingDir + "\\src\\builders\\testcasesForBuilding.txt";
		String expectedOutputFile = workingDir + "\\src\\builders\\expectedResultsForBuilding.txt";
		BuildingATD atd = new BuildingATD(inputFile, expectedOutputFile);
		atd.executeTestCases();
	}
}